package com.kmxd.ams.app.event.efile.command;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.common.collect.Lists;
import com.kmxd.ams.client.archive.IArchiveFieldSvc;
import com.kmxd.ams.client.archive.IArchiveOssSettingSvc;
import com.kmxd.ams.client.archive.IArchiveTypeSvc;
import com.kmxd.ams.client.archive.dto.ArchiveOssSettingQueryDTO;
import com.kmxd.ams.client.archive.vo.ArchiveFieldListVO;
import com.kmxd.ams.client.archive.vo.ArchiveTypeVO;
import com.kmxd.ams.client.arrange.IBoxSvc;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.client.arrange.dto.DocumentDayQueryDTO;
import com.kmxd.ams.client.oss.IEfileRecordSvc;
import com.kmxd.ams.client.oss.vo.EfileRecordListVO;
import com.kmxd.ams.client.system.ISysConfigSvc;
import com.kmxd.ams.client.system.ISysFondsSvc;
import com.kmxd.ams.client.system.vo.SysFondsVO;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.ConfigConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.RedissonConst;
import com.kmxd.ams.core.common.enums.ArchStatusEnum;
import com.kmxd.ams.core.oss.template.OssTemplate;
import com.kmxd.ams.core.util.ConfigUtil;
import com.kmxd.ams.core.util.DynamicTableNameUtil;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.infra.arrange.base.BaseBO;
import com.kmxd.ams.infra.arrange.entity.Document;
import com.kmxd.ams.infra.oss.entity.EfileRecord;
import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 电子文件路径整理
 *
 * @author ZuoLG
 */
@Slf4j
@Component
@AllArgsConstructor
public class EfilePathArrangeCmd {
  private IArchiveOssSettingSvc archiveOssSettingSvc;
  private IArchiveFieldSvc fieldSvc;
  private IEfileRecordSvc efileSvc;
  private IDocumentSvc documentSvc;
  private ISysConfigSvc sysConfigSvc;
  private IBoxSvc boxSvc;
  private IVolumeSvc volumeSvc;
  private OssTemplate ossTemplate;
  private ISysFondsSvc sysFondsSvc;
  private IArchiveTypeSvc archiveTypeSvc;
  private final RedissonClient redissonClient;

  /** 归档 */
  @Transactional(rollbackFor = Exception.class)
  public void archivingEfile(BaseBO req) throws Exception {
    String key =
        RedissonConst.KEY_PREFIX + "pathArrange:" + req.getArchTypeId() + ":" + req.getTableId();
    RLock lock = lock(key);
    if (ObjectUtil.isNull(lock)) {
      return;
    }
    try {

      log.debug("docIds: {} ", req.toString());
      // 获取转换方式
      String transferConf = sysConfigSvc.getConfigContentByKey(ConfigConst.OSS_TRANSFER);
      // 转换后缀
      String transferExt = ConfigUtil.getVal(transferConf, ConfigConst.TRANSFER_EXT);
      // 1、获取分组规则
      ArchiveOssSettingQueryDTO ossQueryDTO =
          BeanUtil.copyProperties(req, ArchiveOssSettingQueryDTO.class);
      List<ArchiveFieldListVO> paths = archiveOssSettingSvc.getPath(ossQueryDTO);
      List<ArchiveFieldListVO> names = archiveOssSettingSvc.getName(ossQueryDTO);
      // 2、根据分组获取路径，更改路径及状态
      List<Document> listDocs = Lists.newArrayList();
      if (StrUtil.isNotBlank(req.getDocIds())) {
        BaseBO baseBO = BeanUtil.copyProperties(req, BaseBO.class);
        baseBO.setDocIds(req.getDocIds());
        DynamicTableNameUtil.setTableName(req.getTableId());
        listDocs = documentSvc.selectListByIds(baseBO);
      } else {
        // 查询档案库需要处理的数据-只查询当天
        DocumentDayQueryDTO documentReq = new DocumentDayQueryDTO();
        documentReq.setArchTypeId(req.getArchTypeId());
        documentReq.setTableId(req.getTableId());
        String startTime = DateUtil.format(DateUtil.date(), "yyyy-MM-dd 00:00:00");
        String endTime = DateUtil.format(DateUtil.date(), "yyyy-MM-dd 23:59:59");
        documentReq.setStartDate(startTime);
        documentReq.setEndDate(endTime);
        listDocs = documentSvc.selectListByDay(documentReq);
      }
      // 定时整理不存在盒、案卷的情况
      List<EfileRecord> efileUpdate = Lists.newArrayList();
      listDocs.forEach(
          item -> {
            item.setArchTypeId(req.getArchTypeId());
            item.setTableId(req.getTableId());
            SysFondsVO fonds = sysFondsSvc.view(item.getFondsId());
            ArchiveTypeVO archiveType = archiveTypeSvc.view(item.getArchTypeId());

            // 获取路径
            String path = getValue(item, paths, fonds, archiveType, true);
            String name = getValue(item, names, fonds, archiveType, false);
            if (StrUtil.isBlank(path) || StrUtil.isBlank(name)) {
              return;
            }
            String tableName = Constants.TABLE_NAME_EFILE + "_" + fonds.getAutoIndex();
            DynamicTableNameUtil.setTableName(tableName);
            List<EfileRecordListVO> efiles =
                efileSvc.selectListByDocIds(
                    req.getArchTypeId(), IdUtil.split(String.valueOf(item.getId())));
            // 增加序号
            AtomicReference<Integer> index = new AtomicReference<>(efiles.size() == 0 ? 0 : 1);
            efiles.forEach(
                efile -> {
                  // 只有未移交整理过的文件才重新路径整理
                  if (ObjectUtil.notEqual(efile.getMoveStatus(), 1L)) {
                    EfileRecord record = BeanUtil.copyProperties(efile, EfileRecord.class);
                    String newPath =
                        ossTemplate.separator()
                            + ossTemplate.getDefPath()
                            + path
                            + ossTemplate.separator();
                    String newName =
                        name + (index.get() == 0 ? "" : "-" + index.get()) + "." + efile.getExt();
                    String newPdfName =
                        name + (index.get() == 0 ? "" : "-" + index.get()) + "." + transferExt;
                    String rootPath =
                        NumberUtil.equals(efile.getArrangeStatus(), ArchStatusEnum.S1.getCode())
                            ? ossTemplate.getManagePath(efile.getDefPath())
                            : ossTemplate.getCollectPath(efile.getDefPath());
                    // 移动普通文件
                    ossTemplate.rename(
                        rootPath + efile.getPath(),
                        ossTemplate.getManagePath(efile.getDefPath()) + newPath + newName);
                    // 如果有转换后的文件，移动转换后的文件
                    if (StrUtil.isNotBlank(efile.getShowFilePath())) {
                      if (StrUtil.equals(transferExt, efile.getExt())) {
                        record.setShowFilePath(newPath + newName);
                      } else {
                        File pdfFile = FileUtil.file(rootPath + efile.getShowFilePath());
                        if (pdfFile.exists()) {
                          ossTemplate.rename(
                              rootPath + efile.getShowFilePath(),
                              ossTemplate.getManagePath(efile.getDefPath()) + newPath + newPdfName);
                          record.setShowFilePath(newPath + newPdfName);
                        }
                      }
                    }
                    record.setSaveFileName(newName);
                    record.setPath(newPath + newName);
                    record.setArrangeStatus(ArchStatusEnum.S1.getCode());
                    record.setMoveStatus(1L);
                    efileUpdate.add(record);
                    index.set(index.get() + 1);
                  }
                });
          });
      efileSvc.updateBatchById(efileUpdate);
      DynamicTableNameUtil.setTableName(req.getTableId());
      documentSvc.updateBatchById(listDocs);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 释放锁
      lock.unlock();
    }
  }

  /**
   * 动态获取分组内容
   *
   * @param document
   * @param keys
   * @param path
   * @return
   */
  private String getValue(
      Document document,
      List<ArchiveFieldListVO> keys,
      SysFondsVO fonds,
      ArchiveTypeVO archiveType,
      boolean path) {
    JSONObject jsonObject = JSONUtil.parseObj(document);
    StringBuffer stringBuffer = new StringBuffer();
    keys.forEach(
        item -> {
          if (path) {
            stringBuffer.append("/");
          }
          if (ObjectUtil.equals(item.getDbFieldKey(), ArchConst.OSS_SETTING_ARCH_TYPE_KEY)) {
            stringBuffer.append(archiveType.getName());
          } else if (ObjectUtil.equals(item.getDbFieldKey(), ArchConst.OSS_SETTING_FONDS_KEY)) {
            stringBuffer.append(fonds.getName());
          } else {
            stringBuffer.append(
                jsonObject.getStr(
                    !StringUtils.isCamel(item.getDbFieldKey())
                        ? StringUtils.underlineToCamel(item.getDbFieldKey())
                        : item.getDbFieldKey()));
          }
        });
    return stringBuffer.toString();
  }

  private RLock lock(String cacheListKey) {
    RLock lock = redissonClient.getLock(cacheListKey);
    try {
      boolean isLocked = lock.tryLock(RedissonConst.WAIT_TIME, TimeUnit.SECONDS);
      if (!isLocked) {
        log.info("附件转换，未获取到锁，不进行转换");
        return null;
      }
    } catch (InterruptedException e) {
      return null;
    }
    return lock;
  }
}
